#pragma once

#include <vector>
#include <memory>
#include <functional>
#include <memory>
#include <thread>
#include "zrpc/net/channel.h"
#include "zrpc/net/socket.h"

#ifdef __APPLE__
#include "zrpc/net/poll.h"
#endif

#ifdef __linux__
#include <sys/eventfd.h>
#include "zrpc/net/epoll.h"
#endif

#ifdef _WIN64
#include "zrpc/net/select.h"
#endif

#include "zrpc/base/timer_queue.h"
#include "zrpc/net/callback.h"

namespace zrpc {
class EventLoop {
 public:
  typedef std::function<void()> Functor;

  EventLoop();

  ~EventLoop();

  void Quit();
  void QuitInLoop();
  void Run();
  void HandleRead();

  void RunInLoop(Functor &&cb);
  void QueueInLoop(Functor &&cb);
  void DelayRunInLoop(Functor &&cb);

  void Wakeup();
  void UpdateChannel(Channel *channel);
  void RemoveChannel(Channel *channel);
  bool HasChannel(Channel *channel);
  void CancelAfter(const std::shared_ptr<Timer> &timer);

  void AssertInLoopThread();

  std::shared_ptr<Timer> RunAfter(double when, bool repeat, TimerCallback &&cb);
  std::shared_ptr<Timer> RunAt(TimeStamp &&stamp, double when, bool repeat,
                               TimerCallback &&cb);

  void HandlerTimerQueue();
  bool IsInLoopThread() const;
  bool GetEventHandling() const;
  std::thread::id GetThreadId() const;
  int GetTimeOut() const;

 private:
  EventLoop(const EventLoop &);

  void operator=(const EventLoop &);

  void AbortNotInLoopThread();
  void DoPendingFunctors();

  std::thread::id thread_id_;
  mutable std::mutex mutex_;
#ifdef __APPLE__
  std::shared_ptr<Poll> epoller_;
  int32_t op_;
  int32_t wakeup_fd_[2];
#endif

#ifdef __linux__
  std::shared_ptr<Epoll> epoller_;
  int32_t wakeup_fd_;
#endif

#ifdef _WIN64
  std::shared_ptr<Select> epoller_;
  int32_t op_;
  int wakeup_fd_[2];
#endif

  std::shared_ptr<TimerQueue> timer_queue_;
  std::shared_ptr<Channel> wakeup_channel_;

  typedef std::vector<Channel *> ChannelList;
  ChannelList active_channels_;
  Channel *current_active_channel_;

  bool running_;
  bool event_handling_;
  bool calling_pending_functors_;
  std::vector<Functor> functors_;
  std::vector<Functor> pending_functors_;
};
}  // namespace zrpc
